/*
 * mapa.c
 *
 *  Created on: 21/07/2014
 *      Author: migue
 */


#include "mapa.h"
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>

MAPA_DATOS* inicializarMapa() {
	MAPA_DATOS* mapa;
	mapa = (MAPA_DATOS*) malloc(sizeof(MAPA_DATOS));

	mapa->_cantBrokers = 0;
	//mapa->_ultBrokerUsado = 1;
	mapa->_ultimoTimeout = TIMEOUT_INICIAL;

	mapa->_cantBusReemp = 0;

	mapa->_cantPersonas= 0;
	mapa->_raiz = NULL;

	mapa->_raizPersona = NULL;

	mapa->_ultBroker = NULL;

	return mapa;
}

void eliminarMapa(MAPA_DATOS* mapa) {
	free(mapa);
}

DATOS_NODO* crearNodoDatos() {
	DATOS_NODO* datos;
	datos = (DATOS_NODO*) malloc(sizeof(DATOS_NODO));
	return datos;
}
void eliminarNodoDatos(DATOS_NODO* datos) {
	free(datos);
}

NODO* crearNodo(DATOS_NODO* datos) {
	NODO* res = (NODO*) malloc(sizeof(NODO));

	res->id = datos->id;
	res->dato = datos;

	res->_derecha = NULL;
	res->_izquierda = NULL;

	return res;
}

int ladoRandom() {
	return rand() % 2;
}


void ponerEnIzquierda(NODO* aPoner, NODO* ladoDerecho) {
	NODO* actual = ladoDerecho;

	if (aPoner == NULL)
		return;

	while (actual != NULL) {
		if (actual->_izquierda == NULL) {
			actual->_izquierda = aPoner;
			actual = NULL;
		}
		else {
			actual = actual->_izquierda;
		}
	}

}

void ponerEnDerecha(NODO* aPoner, NODO* ladoIzquierdo) {
	NODO* actual = ladoIzquierdo;

	if (aPoner == NULL)
		return;

	while (actual != NULL) {
		if (actual->_derecha == NULL) {
			actual->_derecha = aPoner;
			actual = NULL;
		}
		else {
			actual = actual->_derecha;
		}
	}

}



void eliminarNodo(MAPA_DATOS* mapa, int id) {
	NODO* aEliminar = NULL;
	NODO* nodoActual = mapa->_raiz;
	NODO* nodoAnterior = NULL;

	while ( nodoActual != NULL ) {

		if (nodoActual->id == id) {
			aEliminar = nodoActual;
			nodoActual = NULL;
		}
		else {

			if (nodoActual->id < id) {
				nodoAnterior = nodoActual;
				nodoActual = nodoActual->_derecha;
			}
			else if (nodoActual->id > id) {
				nodoAnterior = nodoActual;
				nodoActual = nodoActual->_izquierda;
			}
			else {
				nodoActual = NULL;
			}
		}
	}

	if (aEliminar == NULL)
		return;


	//NODO* aux;

	if (ladoRandom() == 0) {
		//Se eligio el lado izquierdo para que ocupe el lugar del eliminado
		if (aEliminar->_izquierda != NULL) {
			nodoActual = aEliminar->_izquierda;

			if (aEliminar->_derecha != NULL && nodoActual != NULL) {
				ponerEnIzquierda(nodoActual->_derecha, aEliminar->_derecha);
				nodoActual->_derecha = aEliminar->_derecha;
			}
		}
		else {
			//Como el lado izquierdo es NULL se elige el otro lado para ascender
			nodoActual = aEliminar->_derecha;
		}
	}
	else {
		//Se eligio el lado derecho para que ocupe el lugar del eliminado
		if (aEliminar->_derecha != NULL) {

			nodoActual = aEliminar->_derecha;

			if (aEliminar->_izquierda != NULL && nodoActual != NULL) {
				ponerEnDerecha(nodoActual->_izquierda, aEliminar->_izquierda);
				nodoActual->_izquierda = aEliminar->_izquierda;
			}
		}
		else {
			//Como el lado derecho es NULL se elige el otro lado para ascender
			nodoActual = aEliminar->_izquierda;
		}
	}


	if (nodoAnterior != NULL) {
		if (nodoAnterior->id < aEliminar->id) {
			nodoAnterior->_derecha = nodoActual;
		}
		else {
			nodoAnterior->_izquierda = nodoActual;
		}
	}
	else if (nodoActual != NULL) {
		mapa->_raiz = nodoActual;
	}

	// TODO liberar memoria de res
	free(aEliminar->dato);
	free(aEliminar);
}

DATOS_NODO* buscarNodo(MAPA_DATOS* mapa, int id) {
	DATOS_NODO* res = NULL;
	NODO* nodoActual = mapa->_raiz;

	while ( nodoActual != NULL ) {

		if (nodoActual->id == id) {
			res = nodoActual->dato;
			nodoActual = NULL;
		}
		else {

			if (nodoActual->id < id) {
				nodoActual = nodoActual->_derecha;
			}
			else if (nodoActual->id > id) {
				nodoActual = nodoActual->_izquierda;
			}
			else {
				nodoActual = NULL;
			}
		}
	}

	return res;
}

void insertarNodo(MAPA_DATOS* mapa, DATOS_NODO* datos) {

	printf("\t - insertando Nodo \n");

	NODO* nodoActual = mapa->_raiz;

	if (mapa->_raiz == NULL) {
		mapa->_raiz = crearNodo(datos);
		return;
	}


	while (nodoActual != NULL) {

		if (nodoActual->id > datos->id) {

			if (nodoActual->_izquierda == NULL) {
				nodoActual->_izquierda = crearNodo(datos);
				nodoActual = NULL;
			}
			else {
				nodoActual = nodoActual->_izquierda;
			}

		}
		else if (nodoActual->id < datos->id) {

			if (nodoActual->_derecha == NULL) {
				nodoActual->_derecha = crearNodo(datos);
				nodoActual = NULL;
			}
			else {
				nodoActual = nodoActual->_derecha;
			}

		}

	}
}

int compararIP(DireccionHost* A, DireccionHost* B) {
	struct sockaddr_in* sA = (struct sockaddr_in *) A;
	struct sockaddr_in* sB = (struct sockaddr_in *) B;

	int ipA = (int) sA->sin_addr.s_addr;
	int ipB = (int) sB->sin_addr.s_addr;

	printf("\t\t\t ipA: %s \n", inet_ntoa(sA->sin_addr) );
	printf("\t\t\t ipb: %s \n", inet_ntoa(sB->sin_addr) );
	printf("\t\t\t res: %i \n", (ipA-ipB));

	return ipA - ipB;
}

void agregarDebajo(NODO_PERSONA* nodo, DATOS_NODO* datos) {
	NODO_PERSONA* nodoActual = nodo;

	while (nodoActual != NULL) {

		if (nodoActual->_abajo == NULL) {
			nodoActual->_abajo = crearNodoPersona(datos, nodoActual->id_remoto);
			nodoActual = NULL;
		}
		else {
			nodoActual = nodoActual->_abajo;
		}

	}
}

DATOS_NODO* buscarPersona(MAPA_DATOS* mapa, int idRemoto, DireccionHost* ip) {
	DATOS_NODO* res = NULL;
	NODO_PERSONA* nodoActual = mapa->_raizPersona;

	while (nodoActual != NULL) {

		if (nodoActual->id_remoto > idRemoto) {
			nodoActual = nodoActual->_izq;
		}
		else if (nodoActual->id_remoto < idRemoto) {
			nodoActual = nodoActual->_der;
		}
		else if (compararIP(&nodoActual->ip, ip) == 0)	 {
			res = nodoActual->datos;
			nodoActual = NULL;
		}
		else {
			// el id es el mismo por lo que se busca por ip
			nodoActual = nodoActual->_abajo;
		}

	}

	return res;
}

void insertarPersona(MAPA_DATOS* mapa, DATOS_NODO* datosPersona, int idRemoto) {

	printf("\t - insertando persona \n");

	// regreso de la funcion porque ya no es necesario agregar las personas aca.
	return;

	NODO_PERSONA* nodoActual = mapa->_raizPersona;

	if (mapa->_raizPersona == NULL) {
		mapa->_raizPersona = crearNodoPersona(datosPersona, idRemoto);
		return;
	}

	do {
		if (nodoActual->id_remoto > idRemoto) {

			if (nodoActual->_izq == NULL) {
				nodoActual->_izq = crearNodoPersona(datosPersona, idRemoto);
				nodoActual = NULL;
			}
			else {
				nodoActual = nodoActual->_izq;
			}

		}
		else if (nodoActual->id_remoto < idRemoto) {

			if (nodoActual->_der == NULL) {
				nodoActual->_der = crearNodoPersona(datosPersona , idRemoto);
				nodoActual = NULL;
			}
			else {
				nodoActual = nodoActual->_der;
			}

		}
		else if (compararIP(&nodoActual->ip, &datosPersona->ip) != 0) {
			agregarDebajo(nodoActual, datosPersona);
			nodoActual = NULL;
		}
		else {
			nodoActual = NULL;
			perror("ERROR No se pudo agregar correctamente a la persona, porque tiene los mismo datos que una ya existente.");
		}

	} while (nodoActual != NULL);
}

NODO_PERSONA* crearNodoPersona(DATOS_NODO* datos, int idRemoto) {

	printf("\t - creando NodoPersona , idRemoto: %i \n", idRemoto);

	NODO_PERSONA* nodo = (NODO_PERSONA*) malloc(sizeof(NODO_PERSONA));

	nodo->id_remoto = idRemoto;
	copiarDireccion(&nodo->ip, &datos->ip, sizeof(datos->ip));
	nodo->datos = datos;

	nodo->_abajo = NULL;
	nodo->_izq = NULL;
	nodo->_der = NULL;

	return nodo;
}

NODO_BROKER* crearNodoBroker(DATOS_NODO* datos) {
	NODO_BROKER* nodo = (NODO_BROKER*) malloc(sizeof(NODO_BROKER));

	nodo->datos = datos;
	nodo->id = datos->id;

	nodo->_siguiente = NULL;
	nodo->_anterior = NULL;

	return nodo;
}


void agregarNodoBroker(MAPA_DATOS* mapa, NODO_BROKER* nodo) {
	if (mapa->_ultBroker == NULL) {
		mapa->_ultBroker = nodo;
		return;
	}

	if (mapa->_ultBroker->_anterior == NULL ) {
		mapa->_ultBroker->_anterior = nodo;
		nodo->_anterior = mapa->_ultBroker;

		mapa->_ultBroker->_siguiente = nodo;
		nodo->_siguiente = mapa->_ultBroker;
	}
	else {

		NODO_BROKER* nAnterior = mapa->_ultBroker->_anterior;


		nAnterior->_siguiente = nodo;

		nodo->_anterior = nAnterior;
		nodo->_siguiente = mapa->_ultBroker;

		mapa->_ultBroker->_anterior = nodo;

	}

}

void pasarSiguienteBroker(MAPA_DATOS *mapa) {
	if (mapa->_ultBroker != NULL) {
		if (mapa->_ultBroker->_siguiente != NULL)
			mapa->_ultBroker = mapa->_ultBroker->_siguiente;
	}
}


void inhabilitarBroker(MAPA_DATOS* mapa, int idBroker) {
	NODO_BROKER* nodoActual = mapa->_ultBroker;
	NODO_BROKER* nodo = NULL;
	int i = 0;

	while (nodoActual != NULL && i < mapa->_cantBrokers) {

		if (nodoActual->id == idBroker) {
			nodo = nodoActual;
			nodoActual = NULL;
		}
		else {
			nodoActual = nodoActual->_siguiente;
		}

		++i;
	}


	if (nodo != NULL) {
		if (nodo->_anterior != NULL) {
			if (nodo->_anterior != nodo->_siguiente) {

				nodo->_anterior->_siguiente = nodo->_siguiente;
				nodo->_siguiente->_anterior = nodo->_anterior;

				if (nodo == mapa->_ultBroker) {
					mapa->_ultBroker = nodo->_siguiente;
				}

			}
			else {
				mapa->_ultBroker = nodo->_anterior;
				mapa->_ultBroker->_anterior = NULL;
				mapa->_ultBroker->_siguiente = NULL;
			}
		}
		else {
			mapa->_ultBroker = NULL;
		}

		mapa->_cantBrokers--;
		nodo->datos->estadoBroker = BROKER_MUERTO;
		free(nodo);
	}
}

void rehabilitarBroker(DATOS_NODO* datosBroker, DatosEntidad* ent) {
	datosBroker->estadoBroker = BROKER_ACTIVO;
	copiarDireccion(&datosBroker->ip, &ent->direccion, sizeof(datosBroker->ip));
	datosBroker->puerto = ent->puerto_escucha;
}


void copiarDireccion(DireccionHost* dest, DireccionHost* orig, unsigned tam) {
	memcpy( (void*) dest, (void*) orig, tam);
}



void imprimirNodo(NODO* nodo, int espacios) {
	if (nodo == NULL) {
		printf("X");
		return;
	}
	int i;
	for ( i = 0; i < espacios; i++)
		printf(" ");

	printf( "(id:%i-tipo:%i) \n", nodo->dato->id, (int)nodo->dato->tipo);

	for (i = 0; i < espacios; i++)
		printf(" ");

	char a;
	char b;

	if (nodo->_izquierda != NULL)
		a = '|';
	else
		a = ' ';

	if (nodo->_derecha != NULL)
		b = '\\';
	else
		b = ' ';


	printf("   %c          %c\n", a ,b);

	imprimirNodo(nodo->_izquierda, espacios);
	imprimirNodo(nodo->_derecha, espacios + 10);

}

void imprimirMapa(MAPA_DATOS* mapa) {
	printf("\n\n---Estado de Mapa--- \n");
	imprimirNodo(mapa->_raiz, 0);
	printf("\n\n");
}
